package model;

import java.util.ArrayList;


//Checker ng Rank ng each CardSet
public class RankCardSet {
	
	private ArrayList<CardSet> cardSets = new ArrayList<CardSet>();
	
	public RankCardSet() {
		
	}
	
	public ArrayList<CardSet> getCardSets() {
		return cardSets;
	}
	
	public void setCardSets(ArrayList<CardSet> cardSets) {
		this.cardSets = cardSets;
	}

	public void addCardSet(CardSet cardSet) {
		this.cardSets.add(cardSet);
	}
	
	public void rankCheck() {
		boolean done = false;
		
		//ROYAL FLUSH
		done = this.checkForRoyalFlush();
		//STRAIGHT FLUSH
		if(!done)
		done = this.checkForStraightFlush();
		//FOUR OF A KIND
		if(!done)
			done = this.checkForFourKind();
		//FULL HOUSE
		if(!done) 
			done = this.checkForFullHouse();
		//FLUSH
		if(!done) 
			done = this.checkForFlush();
		//STRAIGHT
		if(!done)
			done = this.checkForStraight();
		//THREE OF A KIND
		if(!done) 
			done = this.checkForThreeKind();
		//TWO PAIRS
		if(!done) 
			done = this.checkForTwoPairs();
		//PAIRS
		if(!done)
			done = this.checkForPair();
		//HIGH CARD
		if(!done)
			this.checkForHighCard();
	}
	
	
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	 * 																				 *
	 * Hand Checkers are grouped into 2 functions									 *
	 * One function handles a group of cardset (player cards + table cards)			 *
	 * the other handles a cardset, checking the ranking and the highest combination *
	 * 																				 *
	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
	
	//HIGH CARD
	private void checkForHighCard() {
		int highestValue = 0;
		for(CardSet cardSet: cardSets) {
			if(this.checkSetForHighCard(cardSet) && cardSet.getValue()>=highestValue) {
				highestValue = cardSet.getValue();
			}
		}
		for(CardSet cardSet : cardSets) {
			if(cardSet.getValue()==highestValue) {
				cardSet.setWinner(true);
			}
		}
	}
	private boolean checkSetForHighCard(CardSet cardSet) {
		boolean found = false;
		int totalValue = 0;
		int count = 0;
		for(int i=0; i<13; i++) {
			count = this.countMatchFaces(Deck.face[i], cardSet.getCardSet());
			if(count==1) {
				totalValue = Deck.value[i];
				cardSet.setRanking("High Card");
				cardSet.setValue(totalValue);
				return true;
			}
		}
		return found;
	}
	
	//PAIR SET
	private boolean checkForPair() {
		boolean found = false;
		int highestValue = 0;
		for(CardSet cardSet : cardSets) {
			if(this.checkSetForPair(cardSet) && cardSet.getValue()>=highestValue)
				highestValue = cardSet.getValue();
		}
		if(highestValue>0) {
			for(CardSet cardSet : cardSets) {
				if(cardSet.getValue()==highestValue) {
					cardSet.setWinner(true);
					found = true;
				}
			}
		}
		return found;
	}
	private boolean checkSetForPair(CardSet cardSet) {
		for(int i=0; i<13; i++) {
			if(this.countMatchFaces(Deck.face[i], cardSet.getCardSet())==2) {
				cardSet.setValue(Deck.value[i] * 2);
				cardSet.setRanking("Pair");
				return true;
			}
		}
		return false;
	}
	
	//TWO PAIR SET
	private boolean checkForTwoPairs() {
		boolean found = false;
		int highestValue = 0;
		for(CardSet cardSet: cardSets) {
			if(this.checkSetForTwoPairs(cardSet) && cardSet.getValue()>=highestValue) {
				highestValue = cardSet.getValue();
			}
		}
		if(highestValue>0) {
			for(CardSet cardSet : cardSets) {
				if(cardSet.getValue()==highestValue) {
					cardSet.setWinner(true);
					found = true;
				}
			}
		}
		return found;
	}
	private boolean checkSetForTwoPairs(CardSet cardSet) {
		int count1 = 0;
		int count2 = 0;
		String pair1 = null;
		int totalValue = 0;
		
		for(int i=0; i<13; i++) {
			count1 = this.countMatchFaces(Deck.face[i], cardSet.getCardSet());
			if(count1==2) {
				pair1 = Deck.face[i];
				totalValue = Deck.value[i] * 2;
				for(int j=i+1; j<13; j++) {
					if(!pair1.equals(Deck.face[j])) {
						count2 = this.countMatchFaces(Deck.face[j], cardSet.getCardSet());
						if(count2==2) {
							totalValue += Deck.value[j] * 2;
							cardSet.setRanking("Two Pairs");
							cardSet.setValue(totalValue);
							return true;
						}
					}
				}
				return false;
			}
		}
		return false;
	}
	
	//STRAIGHT SET
	private boolean checkForStraight() {
		boolean found = false;
		int highestValue = 0;
		for (CardSet cardSet : cardSets)
			if(this.checkSetForStraight(cardSet) && cardSet.getHighCard().getValueFace()>=highestValue)
				highestValue = cardSet.getHighCard().getValueFace();
		
		if(highestValue>0)
			for(CardSet cardSet : cardSets)
				if(cardSet.getHighCard()!=null && cardSet.getHighCard().getValueFace()==highestValue) {
					cardSet.setWinner(true);
					found = true;
				}
		
		
		return found;
	}
	private boolean checkSetForStraight(CardSet cardSet) {
		int highFacePosition = 0;
		int countStraight = 0;
		Card highCard = null;
		
		search:
		for(int i=1; i<13; i++)
			for(int j=0; j<4; j++) {
				highCard = this.retrieveCard(Deck.suit[j], Deck.face[i], cardSet.getCardSet());
				if(highCard!=null) {
					highFacePosition = i;
					break search;
				}
			}
		
		if(highFacePosition==9 && this.faceExists(Deck.face[0], cardSet.getCardSet())) {
			countStraight += 2;
			highFacePosition = 9;
			for(int i=10; i<13; i++)
				if(this.faceExists(Deck.face[i], cardSet.getCardSet()))
					countStraight++;
		}
		else if(highFacePosition>0 && highFacePosition<9){
			for(int i=highFacePosition; i<highFacePosition+5; i++)
				if(this.faceExists(Deck.face[i], cardSet.getCardSet()))
					countStraight++;
		}
		else
			return false;
		
		if(countStraight==5) {
			cardSet.setHighCard(highCard);
			cardSet.setRanking("Straight");
			return true;
		}
		
		return false;
	}
	
	//THREE OF A KIND SET
	private boolean checkForThreeKind() {
		boolean found = false;
		int highestValue = 0;
		
		for(CardSet cardSet : this.cardSets) {
			if(this.checkSetForThreeKind(cardSet) && cardSet.getValue()>=highestValue)
				highestValue = cardSet.getValue();
		}
		if(highestValue>0) {
			for(CardSet cardSet : this.cardSets) {
				if(cardSet.getValue()==highestValue)
					cardSet.setWinner(true);
			}
		}
		
		return found;
	}
	private boolean checkSetForThreeKind(CardSet cardSet) {
		for(int i=0; i<13; i++) {
			if(this.countMatchFaces(Deck.face[i], cardSet.getCardSet())==3) {
				cardSet.setValue(Deck.value[i] * 3);
				cardSet.setRanking("Three of a Kind");
				i = 13;
				return true;
			}
		}
		return false;
	}
	
	//FLUSH FUNCTION SET
	private boolean checkForFlush() {
		boolean found = false;
		int highestValue = 0;
		for(CardSet cardSet : this.cardSets) {
			if(this.checkSetForFlush(cardSet) && cardSet.getValue()>=highestValue)
				highestValue = cardSet.getValue();
		}
		if(highestValue>0) {
			for(CardSet cardSet : cardSets) {
				if(cardSet.getValue()==highestValue) {
					cardSet.setWinner(true);
					found = true;
				}
			}
		}
		return found;
	}
	private boolean checkSetForFlush(CardSet cardSet) {
		boolean found = false;
		int count = 0;
		String suit = null;
		//Find If there are 5 cards of the same suit
		for(int i=0; i<4; i++) {
			count = this.countMatchSuits(Deck.suit[i], cardSet.getCardSet());
			if(count>=5) {
				suit = Deck.suit[i];
				cardSet.setRanking("Flush");
				found = true;
				i=4;
			}
		}
		int totalValue = 0;
		int counter = 0;
		//Get Highest Possible Value
		for(int i=0; i<13; i++) {
			if(this.cardExists(suit, Deck.face[i], cardSet.getCardSet())) {
				totalValue += Deck.value[i];
				counter ++;
				if(counter==5)
					i = 12;
			}	
		}
		cardSet.setValue(totalValue);
		return found;
	}
	
	//FULL HOUSE SET
	private boolean checkForFullHouse() {
		boolean found = false;
		int highestValue = 0;
		for(CardSet cardSet: cardSets) {
			if(this.checkSetForFullHouse(cardSet) && cardSet.getValue()>=highestValue) {
				highestValue = cardSet.getValue();
			}
		}
		if(highestValue>0) {
			for(CardSet cardSet : cardSets) {
				if(cardSet.getValue()==highestValue) {
					cardSet.setWinner(true);
					found = true;
				}
			}
		}
		return found;
	}
	private boolean checkSetForFullHouse(CardSet cardSet) {
		//boolean found = false;
		int count3 = 0;
		int count2 = 0;
		String face3 = null;
		//String face2 = null;
		int totalValue = 0;
		
		for(int i=0; i<13; i++) {
			count3 = this.countMatchFaces(Deck.face[i], cardSet.getCardSet());
			if(count3==3) {
				face3 = Deck.face[i];
				totalValue = Deck.value[i] * 3;
				for(int j=i+1; j<13; j++) {
					if(!face3.equals(Deck.face[j])) {
						count2 = this.countMatchFaces(Deck.face[j], cardSet.getCardSet());
						if(count2>=2) {
							//face2 = Deck.face[j];
							totalValue += Deck.value[j] * 2;
							cardSet.setRanking("Full House");
							cardSet.setValue(totalValue);
							return true;
							//found = true;
							//j=13;
						}
					}
				}
				return false;
				//i=13;
			}
		}
		return false;
	}
	
	//FOUR OF A KIND SET
	private boolean checkForFourKind() {
		boolean found = false;
		int highestValue = 0;
		for(CardSet cardSet : cardSets) {
			if(this.checkSetForFourKind(cardSet) && cardSet.getValue()>=highestValue)
				highestValue = cardSet.getValue();
		}
		if(highestValue>0) {
			for(CardSet cardSet: cardSets) {
				if(cardSet.getValue()==highestValue) {
					cardSet.setWinner(true);
					found = true;
				}
			}
		}
		return found;
	}
	private boolean checkSetForFourKind(CardSet cardSet) {
		for(int i=0; i<13; i++) {
			if(this.countMatchFaces(Deck.face[i], cardSet.getCardSet())==4) {
				cardSet.setValue(Deck.value[i] * 4);
				cardSet.setRanking("Four of a Kind");
				//i = 13;
				return true;
			}
		}
		return false;
	}
	
	//STRAIGHT FLUSH SET
	public boolean checkForStraightFlush() {
		boolean found = false;
		int highCardValue = 0;
		for (CardSet cardSet : cardSets)
			if(this.checkSetForStraightFlush(cardSet) && cardSet.getHighCard().getValueFace()>=highCardValue)
				highCardValue = cardSet.getHighCard().getValueFace();
		
		if (highCardValue>0)
			for (CardSet cardSet : cardSets)
				if(cardSet.getHighCard()!=null && cardSet.getHighCard().getValueFace()==highCardValue) {
					cardSet.setWinner(true);
					found = true;
				}
		
		return found;
	}
	private boolean checkSetForStraightFlush(CardSet cardSet) {
		int countSuits = 0;
		int countStraight = 0;
		int highFacePosition = 0;
		String suit = null;
		Card highCard = null;
		
		for(int i=0; i<4; i++) {
			countSuits = this.countMatchSuits(Deck.suit[i], cardSet.getCardSet());
			if(countSuits>=5) {
				suit = Deck.suit[i];
				break;
			}
		}
		if(countSuits>=5) {
			for(int i=1; i<13; i++) {
				highCard = this.retrieveCard(suit, Deck.face[i], cardSet.getCardSet());
				if(highCard!=null) {
					highFacePosition = i;
					break;
				}
			}
			if(highFacePosition==9 && this.cardExists(suit, Deck.face[0], cardSet.getCardSet())) {
				countStraight += 2;
				highFacePosition = 9;
				for(int i=10; i<13; i++)
					if(this.cardExists(suit, Deck.face[i], cardSet.getCardSet()))
						countStraight++;
			}
			else if(highFacePosition>0 && highFacePosition<9) {
				for(int i=highFacePosition; i<highFacePosition+5; i++)
					if(this.cardExists(suit, Deck.face[i], cardSet.getCardSet()))
						countStraight++;
			}
			else
				return false;
			
			if(countStraight==5) {
				cardSet.setHighCard(highCard);
				cardSet.setRanking("Straight Flush");
				return true;
			}
		}
		return false;
	}
	
	//ROYAL FLUSH SET
	private boolean checkForRoyalFlush() {
		for(CardSet cardSet : cardSets) {
			if(this.checkSetRoyalFlush(cardSet)) {
				cardSet.setWinner(true);
				return true;
			}
		}
		return false;
	}
	private boolean checkSetRoyalFlush(CardSet cardSet) {
		boolean found = false;
		int count = 0;
		for(int i=0; i<4; i++) {
			if(this.countMatchSuits(Deck.suit[i], cardSet.getCardSet())>=5) {
				for(int j=0; j<5; j++) {
					if(cardExists(Deck.suit[i], Deck.face[j], cardSet.getCardSet()))
						count++;
				}
				i=4;
			}
		}
		if(count==5) {
			cardSet.setRanking("Royal Flush");
			found = true;
		}
		return found;
	}
	
	//Checks if a card exists in an arraylist of cards that matches the face and suit specified
	private boolean cardExists(String suit, String face, ArrayList<Card> cards) {
		boolean exists = false;
		for(Card card : cards) {
			if(card.getFace().equals(face) && card.getSuit().equals(suit))
				exists = true;
		}
		return exists;
	}
	
	private boolean faceExists(String face, ArrayList<Card> cards) {
		for(Card card : cards)
			if(card.getFace().equals(face))
				return true;
		return false;
	}
	
	private Card retrieveCard(String suit, String face, ArrayList<Card> cards) {
		for(Card card : cards)
			if(card.getFace().equals(face) && card.getSuit().equals(suit))
				return card;
		return null;			
	}
	
	//Find how many matching suits are there to the given suit in an arraylist of cards
	private int countMatchSuits(String suit, ArrayList<Card> cards) {
		int count = 0;
		for(Card card : cards) {
			if(card.getSuit().equals(suit))
				count++;
		}
		return count;
	}
	
	//Find how many matching faces are there to the given face in an arraylist of cards
	private int countMatchFaces(String face, ArrayList<Card> cards) {
		int count = 0;
		for(Card card : cards) {
			if(card.getFace().equals(face))
				count++;
		}
		return count;
	}
	
	//For Debugging
	public void printSet(CardSet cardSet) {
		System.out.println("\n"+cardSet.getPlayer().getName());
		for(Card card : cardSet.getCardSet()) {
			System.out.println(card.getCardName());
		}
	}
}
